#include "curling/rink.hpp"
#include "curling/stone.hpp"

namespace curling {

rink_listener_t::rink_listener_t()
{
}

rink_listener_t::~rink_listener_t()
{
}

rink::rink()
{
}

rink::~rink()
{
}

double rink::width() const
{
  return default_rink::width;
}

double rink::length() const
{
  return default_rink::length;
}

double rink::hack() const
{
  return default_rink::hack;
}

double rink::foot_line() const
{
  return default_rink::foot_line;
}

double rink::tee_line() const
{
  return default_rink::tee_line;
}

double rink::back_line() const
{
  return default_rink::back_line;
}

double rink::hog_line() const
{
  return default_rink::hog_line;
}

double rink::circle() const
{
  return default_rink::circle;
}

double rink::circle0_radius() const
{
  return default_rink::circle0_radius;
}

double rink::circle1_radius() const
{
  return default_rink::circle1_radius;
}

double rink::circle2_radius() const
{
  return default_rink::circle2_radius;
}

double rink::circle3_radius() const
{
  return default_rink::circle3_radius;
}

rink::stone_iterator rink::begin_stones()
{
  return _stones.begin();
}

rink::stone_iterator rink::end_stones()
{
  return _stones.end();
}

void rink::add_stone( boost::shared_ptr<stone> s )
{
  _stones.push_back(s);
}

void rink::remove_stone( boost::shared_ptr<stone> s )
{
  // never can remember this stuff
  assert(false);
}

void rink::simulate( double dt )
{
  for (stone_iterator i=begin_stones(); i!=end_stones(); ++i)
  {
    (*i)->simulate(dt);
  }
  
  handle_collisions(dt);
}

void rink::handle_collisions( double dt )
{
  /*
   * Check for collisions against the walls.
   */
  for (stone_iterator i=begin_stones(); i!=end_stones(); ++i)
  {
    boost::shared_ptr<stone> s = *i;
    if (!s->in_play())
    {
      continue;
    }
    
    if (s->position().x() + s->radius() >= default_rink::width/2.0)
    {
      s->remove_from_play();
    }
    if (s->position().x() - s->radius() <= -default_rink::width/2.0)
    {
      s->remove_from_play();
    }
    if (s->position().y() + s->radius() >= default_rink::length)
    {
      s->remove_from_play();
    }
    if (s->position().y() - s->radius() <= 0.0)
    {
      s->remove_from_play();
    }
  }
  
  /*
   * Check for collisions between stones.
   */
  for (stone_iterator i=begin_stones(); i!=end_stones(); ++i)
  {
    for (stone_iterator j=i+1; j!=end_stones(); ++j)
    {
      boost::shared_ptr<stone> s1 = *i;
      boost::shared_ptr<stone> s2 = *j;
      
      if (s1->in_play() and s2->in_play())
      {
        vector2d s1p = s1->position();
        vector2d s2p = s2->position();
        vector2d delta = s1p - s2p;
        double overlap = (s1->radius()+s2->radius()) - delta.magnitude();
        vector2d dir = delta;
        dir.normalize();
        
        // move appart
        if (overlap > 0.0)
        {
          s1->_position += dir*(overlap/2.0);
          s2->_position -= dir*(overlap/2.0);
        
          // update velocities
          s1p = s1->position();
          s2p = s2->position();
          vector2d s1nvel = s1->velocity();
          s1nvel.normalize();
          vector2d s2nvel = s2->velocity();
          s2nvel.normalize();
          double s1e = dot(s1nvel, dir);
          double s2e = dot(s2nvel, dir);
          double te = std::fabs(s1e)*s1->velocity().magnitude() +
                      std::fabs(s2e)*s2->velocity().magnitude();
          s1->_velocity += dir*te;
          s2->_velocity -= dir*te;
          
          // notify listeners
          for (size_t i=0; i<_listeners.size(); ++i)
          {
            _listeners[i]->stone_stone_collision(s1, s2, te);
          }
        }
      }
    }
  }
}

void rink::add_listener( boost::shared_ptr<rink_listener_t> listener )
{
  _listeners.push_back(listener);
}

void rink::remove_listener( boost::shared_ptr<rink_listener_t> listener )
{
  assert(false);
}

}